package logger

import (
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"sort"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
	"gopkg.in/yaml.v2"
)

var (
	// the local logger
	//logger *zap.SugaredLogger
	defLogger *Logger
	//Log       *zap.Logger
)

type LogConfiguration struct {
	LogCfgs map[string]*LogConfig `yaml:logConfigs`
}

type LogConfig struct {
	ConsoleEnable bool       `yaml:"consoleEnable""`
	Filename      string     `yaml:"filename"`
	MaxSize       int        `yaml:"maxSize"`
	MaxBackups    int        `yaml:"maxBackups"`
	MaxAge        int        `yaml:"maxAge"`
	CallerSkip    int        `yaml:"callerSkip"`
	Zap           zap.Config `yaml:"zap"`
}

type Logger struct {
	conLogLevel  *zap.AtomicLevel
	fileLogLevel *zap.AtomicLevel
	zapLogger    *zap.Logger
	zapSuger     *zap.SugaredLogger
}

func init() {
	//默认加载log.yaml
	//loadConfig("./log.yaml")
	//临时代码
	//if nil == Log {
	if nil == defLogger {
		//设置 初始化过程的log
		module := "init"
		var cfg = new(LogConfig)
		cfg.ConsoleEnable = false
		cfg.Filename = "./log/" + module + ".log"
		cfg.MaxAge = 31
		cfg.MaxSize = 512
		cfg.MaxBackups = 7
		cfg.Zap = zap.NewProductionConfig()
		cfg.CallerSkip = 3
		NewLogger(module, cfg, true)
	}
}
func Init(cfgLog string, module string) {
	loadConfig(cfgLog, module)
}

func GetLogger() *Logger {
	return defLogger
}

func loadConfig(filePath string, module string) {
	cfg := new(LogConfiguration)
	data, err := ioutil.ReadFile(filePath)
	//f, err := os.Open("./flow.yaml");
	//yaml.NewDecoder(f).Decode(&flowCfg)
	if nil != err {
		fmt.Println(err)
		return
	} else {
		yaml.Unmarshal(data, cfg)
	}
	fmt.Println("Logfile", filePath, cfg)
	////加载第一个配置
	//for k, v := range cfg.LogCfgs {
	//	NewLogger(k, v, true)
	//	break //只加载第一个
	//}
	if v, ok := cfg.LogCfgs[module]; ok {
		NewLogger(module, v, true)
	} else {
		fmt.Println("Can't Get the LogModule ", module)
	}
}

func NewLogger(module string, logCfg *LogConfig, bDefault bool) *Logger {
	var logger *Logger
	var allCore []zapcore.Core
	consoleLogLevel := zap.NewAtomicLevel()
	fileLogLevel := zap.NewAtomicLevel()

	//fmt.Println("NewLogger", logCfg)
	//fmt.Println("ConsoleEnable", logCfg.ConsoleEnable)
	//创建 console
	if logCfg.ConsoleEnable {
		consoleConfig := zap.NewProductionEncoderConfig() //生成配置
		// 时间格式
		consoleConfig.EncodeTime = zapcore.ISO8601TimeEncoder
		consoleConfig.EncodeDuration = zapcore.SecondsDurationEncoder
		consoleConfig.EncodeCaller = zapcore.ShortCallerEncoder

		consoleEncoder := zapcore.NewConsoleEncoder(consoleConfig)
		consoleDebugging := zapcore.Lock(os.Stdout)

		consoleLogLevel.SetLevel(zap.DebugLevel)
		consoleCore := zapcore.NewCore(
			consoleEncoder,   //编码器配置
			consoleDebugging, //打印到控制台和文件
			//zap.InfoLevel,    //日志等级
			consoleLogLevel, //日志等级
		)
		allCore = append(allCore, consoleCore)

	}

	//filewriter
	//变更默认设置
	logCfg.Zap.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	logCfg.Zap.EncoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder
	logCfg.Zap.EncoderConfig.EncodeCaller = zapcore.ShortCallerEncoder

	//file log level
	//fileLogLevel.SetLevel(logCfg.Zap.Level)
	fileLogLevel.SetLevel(zap.InfoLevel)
	//lumberjack
	hook := lumberjack.Logger{
		Filename:   logCfg.Filename,
		MaxSize:    logCfg.MaxSize, // megabytes
		MaxBackups: logCfg.MaxBackups,
		MaxAge:     logCfg.MaxAge, //days
		//Compress:   compress, // disabled by default
	}
	fileWriter := zapcore.AddSync(&hook)
	fileCore := zapcore.NewCore(
		zapcore.NewJSONEncoder(logCfg.Zap.EncoderConfig),
		fileWriter,
		fileLogLevel,
	)
	allCore = append(allCore, fileCore)
	core := zapcore.NewTee(allCore...)

	//参数
	if logCfg.Zap.InitialFields == nil {
		logCfg.Zap.InitialFields = make(map[string]interface{})
	}
	logCfg.Zap.InitialFields["module"] = module
	var opts []zap.Option
	if len(logCfg.Zap.InitialFields) > 0 {
		fs := make([]zap.Field, 0, len(logCfg.Zap.InitialFields))
		keys := make([]string, 0, len(logCfg.Zap.InitialFields))
		for k := range logCfg.Zap.InitialFields {
			keys = append(keys, k)
		}
		sort.Strings(keys)
		for _, k := range keys {
			fs = append(fs, zap.Any(k, logCfg.Zap.InitialFields[k]))
		}
		opts = append(opts, zap.Fields(fs...))
	}
	opts = append(opts, zap.AddCaller())
	opts = append(opts, zap.AddCallerSkip(logCfg.CallerSkip))

	//opts = append(opts, zap.AddCaller())
	zapLogger := zap.New(core, opts...) //.WithOptions(zap.AddCaller())
	if nil != zapLogger {
		logger = &Logger{
			zapLogger:    zapLogger,
			conLogLevel:  &consoleLogLevel,
			fileLogLevel: &fileLogLevel,
		}
		logger.zapSuger = logger.zapLogger.Sugar()
		//设置为默认Logger
		if bDefault {
			defLogger = logger
			//Log = logger.zapLogger
		}
	} else {
		logger = nil
	}
	return logger
}

//Logger 函数
//for log
func (l *Logger) Write(p []byte) (n int, err error) {
	//l.zapLogger.Info(string(p))
	l.zapSuger.Info(string(p))
	return len(p), nil
}

func (l *Logger) SetConsoleLevel(level zapcore.Level) {
	l.conLogLevel.SetLevel(level)
}

func (l *Logger) SetFileLevel(level zapcore.Level) {
	l.fileLogLevel.SetLevel(level)
}
func (l *Logger) Debug(msg string, args ...zap.Field) {
	l.zapLogger.Debug(msg, args...)
}

func (l *Logger) Info(msg string, args ...zap.Field) {
	l.zapLogger.Info(msg, args...)
}

func (l *Logger) Warn(msg string, args ...zap.Field) {
	l.zapLogger.Warn(msg, args...)
}

func (l *Logger) Error(msg string, args ...zap.Field) {
	l.zapLogger.Error(msg, args...)
}

func (l *Logger) DPanic(msg string, args ...zap.Field) {
	l.zapLogger.DPanic(msg, args...)
}

func (l *Logger) Panic(msg string, args ...zap.Field) {
	l.zapLogger.Panic(msg, args...)
}

func (l *Logger) Fatal(msg string, args ...zap.Field) {
	l.zapLogger.Fatal(msg, args...)
}

//公共函数
func SetConsoleLevel(level zapcore.Level) {
	if nil != defLogger {
		defLogger.conLogLevel.SetLevel(level)
	}
}

func SetFileLevel(level zapcore.Level) {
	if nil != defLogger {
		defLogger.fileLogLevel.SetLevel(level)
	}
}

func Debug(msg string, args ...zap.Field) {
	if nil != defLogger {
		defLogger.Debug(msg, args...)
	}
}

func Info(msg string, args ...zap.Field) {
	if nil != defLogger {
		defLogger.Info(msg, args...)
	}
}

func Warn(msg string, args ...zap.Field) {
	if nil != defLogger {
		defLogger.Warn(msg, args...)
	}
}

func Error(msg string, args ...zap.Field) {
	if nil != defLogger {
		defLogger.Error(msg, args...)
	}
}

func DPanic(msg string, args ...zap.Field) {
	if nil != defLogger {
		defLogger.DPanic(msg, args...)
	}
}

func Panic(msg string, args ...zap.Field) {
	if nil != defLogger {
		defLogger.Panic(msg, args...)
	}
}

func Fatal(msg string, args ...zap.Field) {
	if nil != defLogger {
		defLogger.Fatal(msg, args...)
	}
}

//i 为结构体，不能是结构体的指针
func StructField(i interface{}) []zap.Field {
	t := reflect.TypeOf(i)
	v := reflect.ValueOf(i)
	var logField []zap.Field
	//log.Info("woker config ", zap.Any("typeof", t), zap.Any("valueof", v))
	for k := 0; k < t.NumField(); k++ {
		switch v.Field(k).Kind() {
		case reflect.String:
			logField = append(logField, zap.String(t.Field(k).Name, v.Field(k).String()))
		case reflect.Bool:
			logField = append(logField, zap.Bool(t.Field(k).Name, v.Field(k).Bool()))
		case reflect.Int:
			logField = append(logField, zap.Any(t.Field(k).Name, v.Field(k).Int()))
		case reflect.Int8:
			logField = append(logField, zap.Any(t.Field(k).Name, v.Field(k).Int()))
		case reflect.Int16:
			logField = append(logField, zap.Any(t.Field(k).Name, v.Field(k).Int()))
		case reflect.Int32:
			logField = append(logField, zap.Any(t.Field(k).Name, v.Field(k).Int()))
		case reflect.Int64:
			logField = append(logField, zap.Any(t.Field(k).Name, v.Field(k).Int()))
		case reflect.Uint:
			logField = append(logField, zap.Any(t.Field(k).Name, v.Field(k).Uint()))
		case reflect.Uint8:
			logField = append(logField, zap.Any(t.Field(k).Name, v.Field(k).Uint()))
		case reflect.Uint16:
			logField = append(logField, zap.Any(t.Field(k).Name, v.Field(k).Uint()))
		case reflect.Uint32:
			logField = append(logField, zap.Any(t.Field(k).Name, v.Field(k).Uint()))
		case reflect.Uint64:
			logField = append(logField, zap.Any(t.Field(k).Name, v.Field(k).Uint()))
		case reflect.Float32:
			logField = append(logField, zap.Any(t.Field(k).Name, v.Field(k).Float()))
		case reflect.Float64:
			logField = append(logField, zap.Any(t.Field(k).Name, v.Field(k).Float()))
		case reflect.Complex64:
			logField = append(logField, zap.Any(t.Field(k).Name, v.Field(k).Complex()))
		case reflect.Complex128:
			logField = append(logField, zap.Any(t.Field(k).Name, v.Field(k).Complex()))
		case reflect.Array:
		case reflect.Chan:
		case reflect.Func:
		case reflect.Interface:
		case reflect.Map:
		case reflect.Ptr:
		case reflect.Uintptr:
		case reflect.Slice:
		case reflect.Struct:
		default:
		}
	}
	return logField
}
